﻿using System;
using System.IO;
using Lucene.Net.Store;
using Microsoft.WindowsAzure;
using Microsoft.WindowsAzure.ServiceRuntime;
using Microsoft.WindowsAzure.StorageClient;

namespace Lucene.Linq.Azure
{
    public class AzureDriveDirectory : FSDirectory
    {
        protected const string DefaultDirectory = "vhd";
        protected const int DefaultSizeInMB = 1000;
        protected const string DefaultVhdName = "lucene.net.vhd";
        protected const string DefaultPath = DefaultDirectory + "\\" + DefaultVhdName;

        private CloudDrive _drive;
        private LocalResource _azureDriveCache;
        private int _sizeInMB;
        private string _path;

        private bool _isMounted;
        private string _localPath;

        protected CloudStorageAccount Account { get; private set; }

        private CloudDrive Drive
        {
            get { return _drive ?? (_drive = Account.CreateCloudDrive(Path)); }
        }

        private LocalResource AzureDriveCache
        {
            get
            {
                if (_azureDriveCache == null)
                {
                    _azureDriveCache = RoleEnvironment.GetLocalResource("DriveLocalCache");
                    CloudDrive.InitializeCache(_azureDriveCache.RootPath + "cache", _azureDriveCache.MaximumSizeInMegabytes);
                }
                return _azureDriveCache;
            }
        }

        protected string Path
        {
            get { return _path ?? (_path = DefaultPath); }
            private set { _path = value; }
        }

        protected int SizeInMB
        {
            get
            {
                if (_sizeInMB == 0)
                    _sizeInMB = DefaultSizeInMB;
                return _sizeInMB;
            }
            private set { _sizeInMB = value; }
        }

        public AzureDriveDirectory(CloudStorageAccount account)
            : this(account, DefaultSizeInMB, DefaultPath)
        { }

        public AzureDriveDirectory(CloudStorageAccount account, int sizeInMB)
            : this(account, sizeInMB, DefaultPath)
        { }

        public AzureDriveDirectory(CloudStorageAccount account, string path)
            : this(account, DefaultSizeInMB, path)
        { }

#pragma warning disable 612,618
        public AzureDriveDirectory(CloudStorageAccount account, int sizeInMB, string path)
#pragma warning restore 612,618
        {
            SizeInMB = sizeInMB;
            Path = path;
            Account = account;
            MountDirectory();
            lockFactory = new NativeFSLockFactory();
        }

        private void MountDirectory()
        {
            MountDrive();
            if (!_isMounted)
            {
                CreateDrive();
                MountDrive();
                if (!_isMounted)
                    throw new DriveNotFoundException("Drive cannot be mounted");
            }
            directory = new DirectoryInfo(_localPath);
        }

        private void MountDrive()
        {
            _localPath = Drive.Mount(AzureDriveCache.MaximumSizeInMegabytes, DriveMountOptions.None);
            _isMounted = (_localPath != null);

            using (StreamReader reader = File.OpenText(GetSizeFilename))
            {
                SizeInMB = Convert.ToInt32(reader.ReadLine());
                reader.Close();
            }
        }

        private void UnmountDrive()
        {
            if (_isMounted) Drive.Unmount();
            _isMounted = false;
        }

        private void CreateDrive()
        {

            Drive.CreateIfNotExist(SizeInMB);
            MountDrive();

            using (StreamWriter writer = File.CreateText(GetSizeFilename))
            {
                writer.WriteLine(SizeInMB);
                writer.Close();
            }

            UnmountDrive();
        }

        private string GetSizeFilename
        {
            get { return string.Format("{0}\\Lucene.Drive.size", Path); }
        }


        public override void Close()
        {
            UnmountDrive();
            base.Close();
        }

        public override void Dispose()
        {
            UnmountDrive();
            base.Dispose();
        }
    }
}
